package com.lfy.modules.safety.blankMenu;

import cn.hutool.extra.spring.SpringUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import com.lfy.domain.anno.Describe;
import com.lfy.modules.common.domain.BaseEnum;
import com.lfy.modules.safety.domain.BlackList;
import com.lfy.modules.safety.mapper.BlackListMapper;
import com.lfy.modules.common.utils.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors;

/**
 * 黑名单缓存
 *
 * @author <a href="https://www.lvfeiy.top">lvfeiyang</a>
 * @date 2024/4/26 1:25
 */
@Slf4j
@Describe("黑名单校对任务")
public class BlackMenuCache implements Job {
    private static final Cache<String, LongAdder> BLACK_CACHE;
    private static final CopyOnWriteArraySet<String> BLACK_MENU = new CopyOnWriteArraySet<>();
    private static final ThreadPoolExecutor MAIN_THREAD_POOL;
    private static final BlackListMapper BLACKLIST_MAPPER;
    /**
     * 触发黑名单的阈值
     */
    private static final int THRESHOLD_VALUE = 20;

    static {
        //五分钟内，相同ip限流次数超过20次，加入黑名单
        BLACK_CACHE = Caffeine.newBuilder()
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .initialCapacity(64)
                .maximumSize(500)
                .build();
        MAIN_THREAD_POOL = SpringUtil.getBean("mainThreadPool");
        BLACKLIST_MAPPER = SpringUtil.getBean(BlackListMapper.class);
        initBlackMenu();
        log.info("BlackMenuCache init success");
    }

    public static void setBlack(String key) {

        if (Objects.isNull(key) || key.contains(RequestUtil.LOCAL_REMOTE_HOST1) || key.contains(RequestUtil.LOCAL_REMOTE_HOST2)
                || key.contains(RequestUtil.LOCAL_REMOTE_HOST3) || key.contains(RequestUtil.LOCAL_REMOTE_HOST4)) {
            //本地不限制
            return;
        }
        Optional.ofNullable(BLACK_CACHE.get(key, (v) -> new LongAdder())).ifPresent(LongAdder::increment);
    }

    public static long getQuantity(String key) {
        if(Objects.isNull(key)){
            return 0;
        }
        LongAdder longAdder = BLACK_CACHE.get(key, (v) -> new LongAdder());
        assert longAdder != null;
        return longAdder.longValue();
    }

    /**
     * 是否触发黑名单阈值
     * @param key ip
     * @return true 触发
     */
    public static boolean isTriggerBlack(String key) {
        if(Objects.isNull(key)){
            return false;
        }
        long quantity = getQuantity(key);
        return quantity >= THRESHOLD_VALUE;
    }

    /**
     * 加入黑名单
     * @param key ip
     */
    public static void triggerBlack(String key) {
        if(Objects.isNull(key)){
            return;
        }
        BlackList blackList = new BlackList();
        blackList.setIp(key);
        blackList.setBecause(BaseEnum.BlackEnum.BLACK_LIMIT.getCode());
        blackList.setIpAddress(RequestUtil.getPlace(key));
        BLACKLIST_MAPPER.insert(blackList);
        addBlackMenu(key);

    }

    public static void addBlackMenu(String key) {
        if(Objects.isNull(key)){
            return;
        }
        BLACK_MENU.add(key);
    }

    public static boolean isContains(String key) {

        return BLACK_MENU.contains(key);
    }

    public static void removeBlackMenuByKey(String key) {
        BLACK_MENU.remove(key);
    }

    public static void clearBlackMenu() {
        BLACK_MENU.clear();
    }

    /**
     * 初始化黑名单列表
     */
    public static void initBlackMenu() {
        List<BlackList> blackLists = BLACKLIST_MAPPER.selectList(null);
        CopyOnWriteArraySet<String> set = blackLists.stream().map(BlackList::getIp).collect(Collectors.toCollection(CopyOnWriteArraySet::new));
        BLACK_MENU.addAll(set);
    }

    public static List<BlackList> getAllBlackList() {

        return BLACKLIST_MAPPER.selectList(null);
    }

    /**
     * 获取还未到达黑名单阈值的ip，并按照触发限流次数排序，key为数量，value为ip
     */
    public static TreeMap<Long, String> getBlackSortMap(){
        ConcurrentMap<@NonNull String, @NonNull LongAdder> stringLongAdderConcurrentMap = BLACK_CACHE.asMap();
        if(stringLongAdderConcurrentMap.isEmpty()){
            return null;
        }
        TreeMap<Long, String> result = new TreeMap<>();
        stringLongAdderConcurrentMap.forEach((k,v)->result.put(v.longValue(),k));
        return result;
    }
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        initBlackMenu();
    }
}
